﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace BullShitPokerServer
{
    class Game : IGame
    {
        #region Propetries
        public int Round
        {
            get
            {
                lock (locker) return round;
            }
            private set
            {
                lock (locker) round = value;
            }
        }
        public string Chat
        {
            get
            {
                lock (chat) return chat.Trim('\n', ' ');
            }
            private set
            {
                lock (chat) chat = value;
            }
        }
        public bool Started
        {
            get
            {
                lock (locker) return started;
            }
            private set
            {
                lock (locker) started = value;
            }
        }
        public string NextMovePlayer
        {
            get
            {
                lock (locker)
                {
                    if (Players.Count == 0) return "";
                    if (nextMove >= Players.Count) nextMove = 0;
                    return Players[nextMove].Name;
                }
            }
        }
        public bool StartedRoundMove
        {
            get
            {
                lock (locker) return startedRoundMove;
            }
            private set
            {
                lock (locker) startedRoundMove = value;
            }
        }
        public bool RoundStarted
        {
            get
            {
                lock (locker) return roundStarted;
            }
            private set
            {
                lock (locker) roundStarted = value;
            }
        }
        public string LastMove
        {
            get
            {
                lock (locker)
                {
                    if (Moves.Count == 0) return "";
                    return Moves[Moves.Count - 1];
                }
            }
        }
        public int CountCardsAll
        {
            get
            {
                int res = 0;
                lock (Players)
                    for (int i = 0; i < Players.Count; i++) res += Players[i].CountCards;
                return res;
            }
        }
        #endregion

        List<Player> Players = new List<Player>();
        List<string> Moves = new List<string>();
        Dictionary<string, Socket> Sockets = new Dictionary<string, Socket>();
        public delegate void delegateSend(string msg);
        public delegate List<Socket> delegateClients();
        private delegateSend Send;
        private delegateClients Clients;

        string chat = "";
        int round = 0;
        bool started = false;
        bool startedRoundMove = false;
        bool roundStarted = false;
        int firstMove = -1;
        int nextMove = 0;
        object locker = new object();

        public Game(delegateSend send, delegateClients clients)
        {
            Round = 0;
            Chat = "";
            Send = send;
            Clients = clients;
            Thread th = new Thread(delegate()
            {
                while (true)
                {
                    while (!StopGame()) ;
                    while (StartGame()) ;
                }
            });
            th.Start();
        }

        private void setChat(string msg)
        {
            Chat += '\n' + msg;
            sendChat(msg);
        }
        private void sendChat(string msg)
        {
            Send("Chat\n" + msg);
        }
        private void sendRoundStart()
        {
            Send("RoundStart\n");
        }
        private void sendNextMove()
        {
            Send("NextMove\n" + NextMovePlayer);
        }
        private void sendStopGame()
        {
            Send("StopGame");
            Console.WriteLine("Game has finished");
        }


        private void nextPlayer()
        {
            lock (locker)
            {
                int k = 0;
                do
                {
                    nextMove++;
                    if (nextMove >= Players.Count) nextMove = 0;
                    k++;
                }
                while (Players[nextMove].Cards.Length == 0 && k <= Players.Count);
            }
        }

        private int getId(string name) {
            lock (locker)
            {
                for (int i = 0; i < Players.Count; i++)
                {
                    if (Players[i].Name == name) return i;
                }
            }
            return -1;
        }

        public bool AddPlayer(string name)
        {
            lock (locker)
            {
                if (Players.Count >= 10) return false;
                if (getId(name) >= 0) return false;
                Players.Add(new Player(name));
            }
            setChat("Player " + name + " has joined the game!\n");
            if (!Started) StartGame();
            return true;
        }

        public bool RemovePlayer(string name)
        {
            int id;
            lock (locker)
            {
                id = getId(name);
                bool next = NextMovePlayer == name;
                if (id < 0) return false;
                Players.RemoveAt(id);
                if (Players.Count == 0) return true;
                if (next)
                {
                    Moves.Add("");
                    nextPlayer();
                    sendNextMove();
                }
            }
            setChat("Player " + name + " has left the game!\n");
            return true;
        }

        public void Remove(Socket s){
            foreach (string name in Sockets.Keys)
            {
                if (Sockets[name] == s)
                {
                    RemovePlayer(name);
                    return;
                }
            }
        }
        
        public string MyCards(string name)
        {
            int id;
            lock (locker)
            {
                id = getId(name); 
                if (id < 0) return "";
                return Players[id].Cards;
            }            
        }

        public bool StopGame()
        {
            lock (locker)
            {
                if (!Started) return false;
                if (Players.Count < 2)
                {
                    if (Clients().Count < 2)
                    {
                        Send("StopServer");
                    }
                    Started = false;
                    sendStopGame();
                    if (Players.Count > 0)
                    {
                        sendChat("Player " + Players[0].Name + " has won!");
                        Console.WriteLine("Player " + Players[0].Name + " has won!");
                    }
                    Moves.Clear();
                    Players.Clear();
                    StartGame();
                    return true;
                }
                return false;
            }
        }

        public bool StartGame()
        {
            if (Started) return false;
            lock (locker) if (Players.Count < 2) return false;
            lock (locker) 
            {
                Round = 0;
                if(!StartRound()) return false;
                setChat("Game has started!\n");
                Console.WriteLine("Game has started!");
                return Started = true;
            }
        }

        public bool StartRound()
        {
            lock (locker)
            {
                if (Players.Count < 2) return false;
                Moves.Clear();
                List<string> Cards = new List<string>();
                for (int i = 0; i < Players.Count; i++)
                {
                    Players[i].Cards = "";
                    for (int j = 0; j < Players[i].CountCards; j++)
                    {
                        string s = "";
                        do
                        {
                            Random Gen = new Random(DateTime.Now.Millisecond);
                            int c = Gen.Next(0, 13);
                            int t = Gen.Next(0, 4);
                            s = c + "," + t;
                        }
                        while (Cards.Contains(s));
                        Cards.Add(s);
                        Players[i].Cards += s + "|";
                    }
                    if (Players[i].Cards.EndsWith("|"))
                        Players[i].Cards = Players[i].Cards.Remove(
                            Players[i].Cards.Length - 1);
                }
                Round++;
                setChat("Round #" + Round + " has started!\n");
                firstMove++;
                if (firstMove >= Players.Count) firstMove = 0;
                nextMove = firstMove;
                StartedRoundMove = false;
                RoundStarted = true;
                sendRoundStart();
                return true;
            }
        }

        public string Move(string move)
        {
            lock (locker)
            {
                Moves.Add(move);
                    string s = "\nPlayer " + move.Split('|')[0] + " has made move '" + Worker.Move(move) + "'";
                    setChat(s + "\n");
                StartedRoundMove = true;
                nextPlayer();
                sendNextMove();
                return s;
            }
        }

        public bool No(string name)
        {
            lock (locker)
            {
                if (getId(name) < 0) return false;
                if (Moves.Count == 0 || (Moves.Count > 0 && Moves.Last() == ""))
                {
                    Players[getId(name)].CountCards--;
                    setChat("Player " + name + " looses!\n");
                    if (Players[getId(name)].CountCards == 0) RemovePlayer(name);
                    nextPlayer();
                    StartRound();
                    return false;
                }
                string last = Moves[Moves.Count - 1];
                string[] data = last.Split('|');
                string prName = data[0];
                string type = data[1];
                string card1 = data[2];
                string card2 = data[3];
                string allCards = "";
                bool result;
                for (int i = 0; i < Players.Count; i++)
                {
                    allCards += Players[i].Cards + "|";
                }
                if (allCards.EndsWith("|")) allCards = allCards.Remove(allCards.Length - 1);
                switch (type)
                {
                    case "0":
                        result = Worker.oneCard(allCards, card1);
                        break;
                    case "1":
                        result = Worker.onePair(allCards, card1);
                        break;
                    case "2":
                        result = Worker.twoPair(allCards, card1, card2);
                        break;
                    case "3":
                        result = Worker.setCard(allCards, card1);
                        break;
                    case "4":
                        result = Worker.Flash(allCards, card2);
                        break;
                    case "5":
                        result = Worker.Street(allCards, card1);
                        break;
                    case "6":
                        result = Worker.FullHouse(allCards, card1, card2);
                        break;
                    case "7":
                        result = Worker.fourCard(allCards, card1);
                        break;
                    case "8":
                        result = Worker.StreetFlash(allCards, card1, card2);
                        break;
                    case "9":
                        result = Worker.RoyalFlash(allCards, card2);
                        break;
                    case "10":
                        result = Worker.fiveCard(allCards, card1);
                        break;
                    case "11":
                        result = Worker.sixCard(allCards, card1);
                        break;
                    case "12":
                        result = Worker.sevenCard(allCards, card1);
                        break;
                    case "13":
                        result = Worker.eightCard(allCards, card1);
                        break;
                    default:
                        result = false;
                        break;
                }
                string chat = "Player " + name + " hasn't believed in '" + Worker.Move(last) + "' and he ";
                if (result)
                {
                    Players[getId(name)].CountCards--;
                    chat += "looses!\n";
                    if (Players[getId(name)].CountCards == 0) RemovePlayer(name);
                }
                if (!result)
                {
                    Players[getId(prName)].CountCards--;
                    chat += "wins!\n";
                    if (Players[getId(prName)].CountCards == 0) RemovePlayer(prName);
                }
                sendChat(chat);
                nextPlayer();
                StartRound();
                return result;
            }
        }

        public bool Say(string name, string text)
        {
            string time = DateTime.Now.TimeOfDay.ToString();
            time = time.Remove(time.IndexOf('.'));
            setChat("(" + time + ") Player " + name + " says: " + text + "\n");
            return true;
        }

    }
}
